/**
    @author: administrator
    @date:   2020/12/4
    @note:
**/
package server

import (
	"fmt"
	"github.com/gorilla/websocket"
	"net"
	"net/http"
	"net/url"
	"time"
	"websocket/impl"
)

//详细的连接信息
type ConnInfo struct {
	conn *impl.Connection
	UserInfo impl.UserInfo
}

var (
	//定义 websocket 的转换器
	upgrader = websocket.Upgrader{
		// 握手过程中 如果需要跨域 会允许跨域
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
	ConnPoll [] *ConnInfo //连接池
)

//socket
func WsHandler(w http.ResponseWriter,r *http.Request){
	var (
		wsConn *websocket.Conn
		err error
		conn *impl.Connection
		data []byte
		connPollItem *ConnInfo  //用户信息跟socket链接组合
		query url.Values
		isNemUser bool = true
	)


	query=r.URL.Query()

	// 连接 webSocket 服务器
	if wsConn,err = upgrader.Upgrade(w,r,nil);err!=nil{
		return
	}
	// 初始化连接 封装底层细节 开放线程安全的API
	if conn,err= impl.InitConnection(wsConn);err!=nil{
		goto ERR
	}

	for _,item:=range ConnPoll{
		//如果不是新用户
		if item.UserInfo.Uid == query.Get("uid"){
			item.conn = conn
			isNemUser = false
			connPollItem = item
			break
		}
	}
	if isNemUser{
		//得到用户信息
		connPollItem = &ConnInfo{
			conn,
			(&impl.UserInfo{}).CreateUserInfo(query,impl.ClientPublicIP(r)),
		}
		for _,item:=range ConnPoll{
			//连接存在 直接跳过
			if item.UserInfo.Uid ==connPollItem.UserInfo.Uid{
				goto ERR
			}
		}
		ConnPoll = append(ConnPoll, connPollItem)
		fmt.Println("新用户进入：",connPollItem.UserInfo,len(ConnPoll))

		// 开启协程心跳
		go heartbeat(connPollItem)

	}
	connPollItem.NewUserAccess()//新用户进入


	//循环 读取 返回消息
	for {
		//从 chan 中读取消息
		if data,err=connPollItem.conn.ReadMessage();err!=nil{
			goto ERR
		}
		//逻辑处理
		doEvent(data,connPollItem.UserInfo)
	}

ERR:
	//TODO 如果出错 就关闭链接
	conn.Close()
}

func RemoteIp(req *http.Request) string {
	remoteAddr := req.RemoteAddr
	if ip := req.Header.Get("XRealIP"); ip != "" {
		remoteAddr = ip
	} else if ip = req.Header.Get("XForwardedFor"); ip != "" {
		remoteAddr = ip
	} else {
		remoteAddr, _, _ = net.SplitHostPort(remoteAddr)
	}

	if remoteAddr == "::1" {
		remoteAddr = "127.0.0.1"
	}

	return remoteAddr
}


//心跳协程
func heartbeat(ConnInfo *ConnInfo){
	var (
		err error
	)
	for{
		msg:=impl.Result(impl.MessageInfo{
			MType:    1,
			Message:  "heartbeat",
		},impl.UserInfo{})
		if err=ConnInfo.conn.WriteMessage(msg);err!=nil{
			fmt.Println("心跳出错：",err)
			ConnInfo.UserLogout()//发送聊天退出的消息
			return
		}
		//每隔1秒 发送一条消息给客户端
		time.Sleep(2*time.Second)
	}
}

//新用户进入
func (ConnInfo *ConnInfo)NewUserAccess(){
	var UserList []impl.UserInfo

	for _,item:=range ConnPoll{
		UserList = append(UserList, item.UserInfo)
	}
	msg:=impl.Result2(impl.MessageInfo{
		MType:    1,
		Message:  "login",
	},ConnInfo.UserInfo,UserList)
	//通知大家
	broadcast(msg)
}

//用户退出
func (ConnInfo *ConnInfo)UserLogout(){
	//先将自己从聊天池中移除
	ConnInfo.Delete()
	var UserList []impl.UserInfo
	for _,item:=range ConnPoll{
		UserList = append(UserList, item.UserInfo)
	}
	msg:=impl.Result2(impl.MessageInfo{
		MType:    1,
		Message:  "logout",
	},ConnInfo.UserInfo,UserList)
	//通知大家
	broadcast(msg)
}

//移除
func (ConnInfo *ConnInfo)Delete(){
	for k,item :=range ConnPoll{
		if item == ConnInfo{
			ConnPoll = append(ConnPoll[:k],ConnPoll[k+1:]...)
			break
		}
	}
}

//程序处理
func doEvent(data []byte,info impl.UserInfo){
	MessageInfo,err:=impl.Parse(data)
	if err!=nil{
		fmt.Println("消息接收错误:",err)
	}
	//1 系统消息  2 群聊消息  3 私聊消息  4 系统操作
	switch MessageInfo.MType {
	case 1: //系统消息
	case 2://群聊消息
		msg:= impl.Result(MessageInfo,info)
		broadcast(msg)
	case 3://私聊消息
		msg:= impl.Result(MessageInfo,info)
		broadcast(msg)
	case 4://系统操作
	}
}

//数据广播
func broadcast(msg []byte){
	if msg==nil{
		fmt.Println("数据广播发送失败")
		return
	}
	for _,connItem:=range ConnPoll{
		if err := connItem.conn.WriteMessage(msg);err!=nil{
			connItem.conn.Close() //关闭连接
		}
	}
}